"""
映射 | 基类
"""

from models import (
    POPage,
    TVO,
    POBase,
    TVOPage,
    VOPage,
)
from my_typing import *
from service.base import TPO
from utils.converter_utils import TimeConverter


class empty:  # noqa: ignore[func name]
    """
    该类用于表示没有为给定的输入或输出值提供数据
    它是必需的，因为 `None` 可能是一个有效的输入或输出值
    """

    pass


class FieldMap(NamedTuple):
    """ 字段映射 """

    attr_list: ListStr
    """ 属性字段列表 """

    default: Any
    """ 默认值 """

    func: CallableAny | None
    """ 转换方法 """


def get_field_map(
    attr_list: ListStr | None = None,
    *,
    default: Any = empty,
    func: CallableAny | None = None,
) -> FieldMap:
    """ 获取映射 """

    return FieldMap(
        attr_list=attr_list or [],
        default=default,
        func=func,
    )


class MappingBase:
    """ 映射 | 基类 """

    id_field: str = "id"
    """ ID 字段名称 """

    po: type[TPO]
    """ PO 对象 """

    list_vo: type[TVO]
    """ 列表页 VO 对象 """

    detail_vo: type[TVO]
    """ 详情页 VO 对象 """

    vo_page: type[TVOPage]
    """ VO 列表分页对象 """

    list_mapping: dict[str, FieldMap] = {}
    """ 列表页 特殊字段映射 """

    detail_mapping: dict[str, FieldMap] = {}
    """ 详情页 特殊字段映射 """

    @staticmethod
    def get_field_value(
        po: TPO,
        field_map: FieldMap,
    ) -> Any:
        """
        获取字段值

        Args:
            po: PO
            field_map: 字段映射

        Returns:
            最终的字段值
        """

        value: Any = None

        # 获取属性列表中的数据
        if field_map.attr_list:
            value = po

        for attr in field_map.attr_list:
            value = getattr(value, attr, None)

        if (
            value is None
            and field_map.default is not empty
        ):
            value = field_map.default

        if field_map.func:
            value = field_map.func(value)

        return value

    @classmethod
    def get_list_dic(cls, po: TPO) -> DictStrAny:
        """ 获取列表页字典 """

        base_dic: DictStrStr = {}

        if isinstance(po, POBase):
            base_dic = {
                cls.id_field: po.id,
                "create_time": TimeConverter.datetime_2_datetime_str(po.create_time),
                "update_time": TimeConverter.datetime_2_datetime_str(po.update_time),
            }

        return {
            **po.model_dump(),
            **base_dic,
            **{
                field: cls.get_field_value(po, field_map)
                for field, field_map in cls.list_mapping.items()
            },
        }

    @classmethod
    def get_detail_dic(cls, po: TPO) -> DictStrAny:
        """ 获取详情页字典 """

        return {
            **cls.get_list_dic(po),
            **{
                field: cls.get_field_value(po, field_map)
                for field, field_map in cls.detail_mapping.items()
            },
        }

    @classmethod
    def po_2_list_vo(cls, po: TPO) -> TVO:
        """ PO -> 列表页 VO """

        return cls.list_vo(**cls.get_list_dic(po))

    @classmethod
    def po_2_detail_vo(cls, po: TPO) -> TVO:
        """ PO -> 详情页 VO """

        return cls.detail_vo(**cls.get_detail_dic(po))

    @classmethod
    def po_page_2_vo_page(cls, page: POPage[TPO]) -> VOPage[TVO]:
        """ PO -> VO 列表分页 """

        vo_list: list[TVO] = [
            cls.po_2_list_vo(_po)
            for _po in page.data_list
        ]

        # 排除 data_list 字段，Pydantic 模型的 .dict(v2 中是 .model_dump) 方法无法处理生成器，会有 TypeError: cannot create 'generator' instances

        return cls.vo_page(
            **page.model_dump(exclude={"data_list"}),
            data_list=vo_list,
        )


TMapping = TypeVar("TMapping", bound=MappingBase, covariant=True)
""" 继承 MappingBase 的类（支持协变） """
